#include "TextureParticleRender.h"
#include <cassert>
#pragma comment(lib,"SOIL.lib")

static float float_rand(void) { return rand() / (float)RAND_MAX; }
#define RANDOM_RANGE(lo, hi) ((lo) + (hi - lo) * float_rand())

TextureParticleRender::TextureParticleRender(const std::string& name) :IRender(name)
{
	mHandle = ShaderManager::Instance()->CreateShader(SID_SAMPLE_16_VERTEX);
	ShaderManager::Instance()->LoadShader(mHandle);
	mHandleFragment = ShaderManager::Instance()->CreateShader(SID_SAMPLE_16_FRAGMENT);
	ShaderManager::Instance()->LoadShader(mHandleFragment);
	mVertexProgram = ShaderManager::Instance()->GetProgram(mHandle);
	mvp = cgGetNamedParameter(mVertexProgram, "mvp");
	mFragProgram = ShaderManager::Instance()->GetProgram(mHandleFragment);
	mDecal = cgGetNamedParameter(mFragProgram, "decal");

	glViewport(0, 0, 400, 400);
	glClearColor(0.1, 0.1, 0.5, 0);
	mAlived = 0;
	mPooled = COUNT_PARTICLES;
	for (int i = 0; i < COUNT_PARTICLES; ++i)
	{
		mPools[i] = &mFixed[i];
	}
	mTime = 0;
	mIntervalTime = 1000 / 60;
	createLocalTexture("dot.jpg");
	glBindTexture(GL_TEXTURE_2D, mTextureID);
	cgGLSetTextureParameter(mDecal, mTextureID);
}

TextureParticleRender::~TextureParticleRender()
{

}

void TextureParticleRender::createParticle()
{
	const int addTiveParticle = 5;
	for (int i = 0; i < addTiveParticle && mPooled > 0; ++i)
	{
		--mPooled;
		mAlives[mAlived] = mPools[mPooled];
		initParticleItem(mAlives[mAlived]);
		++mAlived;
	}
}

void TextureParticleRender::initParticleItem(ParticleItem* particle)
{
	particle->birthPosition[0] = RANDOM_RANGE(-0.25, 0.25);
	particle->birthPosition[1] = -0.50f;
	particle->birthPosition[2] = RANDOM_RANGE(-0.25, 0.25);

	particle->initSpeed = RANDOM_RANGE(1, 5);

	particle->initV[0] = RANDOM_RANGE(-1, 1);
	particle->initV[1] = RANDOM_RANGE(0, 6);
	particle->initV[2] = RANDOM_RANGE(-0.5, 0.5);

	particle->birthTime = mTime;
	particle->deathTime = mTime + 1000;
}

void TextureParticleRender::checkParticleDeath()
{
	for (int i = 0; i < mAlived; ++i)
	{
		ParticleItem& particle = *mAlives[i];
		if (particle.deathTime < mTime)
		{
			ParticleItem* tmp = mAlives[i];
			mAlives[i] = mAlives[mAlived - 1];
			mAlives[mAlived - 1] = tmp;
			--i;
			--mAlived;
			mPools[mPooled] = mAlives[mAlived];
			++mPooled;
		}
	}
}

void TextureParticleRender::createLocalTexture(const char* textureFilePath)
{
	int w, h;
	unsigned char* pData = SOIL_load_image(textureFilePath, &w, &h,0, SOIL_LOAD_RGB);
	if (nullptr != pData)
	{
		glGenTextures(1, &mTextureID);
		glBindTexture(GL_TEXTURE_2D, mTextureID);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pData);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
		glBindTexture(GL_TEXTURE_2D, 0);
		SOIL_free_image_data(pData);
		cgGLSetTextureParameter(mDecal, mTextureID);
	}
}

void TextureParticleRender::idle()
{
	mTime += mIntervalTime;
	createParticle();
	checkParticleDeath();
}

void TextureParticleRender::draw()
{
	cgGLSetStateMatrixParameter(mvp,
		CG_GL_MODELVIEW_PROJECTION_MATRIX, CG_GL_MATRIX_IDENTITY);

	ShaderManager::Instance()->BindShader(mHandle);
	ShaderManager::Instance()->EnableShaderProfile(mHandle);

	ShaderManager::Instance()->BindShader(mHandleFragment);
	ShaderManager::Instance()->EnableShaderProfile(mHandleFragment);

	const float half = 0.02;

	glBegin(GL_QUADS);
	for (int i = 0; i < mAlived; ++i)
	{
		ParticleItem& particle = *mAlives[i];
		float deltaTime = mTime - particle.birthTime;
		glVertex3f(-half + particle.birthPosition[0], particle.birthPosition[1], -half + particle.birthPosition[2]);
		glColor4f(particle.initV[0], particle.initV[1], particle.initV[2], particle.initSpeed);
		glTexCoord3f(0, 0, deltaTime);

		glVertex3f(half + particle.birthPosition[0], particle.birthPosition[1], -half + particle.birthPosition[2]);
		glColor4f(particle.initV[0], particle.initV[1], particle.initV[2], particle.initSpeed);
		glTexCoord3f(1, 0, deltaTime);

		glVertex3f(half + particle.birthPosition[0], particle.birthPosition[1], half + particle.birthPosition[2]);
		glColor4f(particle.initV[0], particle.initV[1], particle.initV[2], particle.initSpeed);
		glTexCoord3f(1, 1, deltaTime);

		glVertex3f(-half + particle.birthPosition[0], particle.birthPosition[1], half + particle.birthPosition[2]);
		glColor4f(particle.initV[0], particle.initV[1], particle.initV[2], particle.initSpeed);
		glTexCoord3f(0, 1, deltaTime);
	}
	glEnd();

	ShaderManager::Instance()->UnBindShader(mHandleFragment);
	ShaderManager::Instance()->DisableShaderProfile(mHandleFragment);

	ShaderManager::Instance()->UnBindShader(mHandle);
	ShaderManager::Instance()->DisableShaderProfile(mHandle);
}